﻿using System;
using System.Linq;
using System.Linq.Expressions;
using NHibernate.Linq;
using SellAndBuy.Domain.Core;

namespace SellAndBuy.Infrastructure.Data.Core.Finders
{
    /// <summary>
    /// NHibernate Repository
    /// </summary>
    /// <typeparam name="TEntity">Entity type</typeparam>
    public class Finder<TEntity> : IFinder<TEntity> where TEntity : class
    {
        protected readonly INhibernateUnitOfWork _unitOfWork;

        /// <summary>
        /// Repository Constructor
        /// </summary>
        /// <param name="unitOfWork">Unit of work</param>
        /// <exception cref="ArgumentNullException"><paramref name="unitOfWork" /> is <c>null</c>.</exception>
        protected Finder(INhibernateUnitOfWork unitOfWork)
        {
            if (unitOfWork == null)
                throw new ArgumentNullException("unitOfWork");

            _unitOfWork = unitOfWork;
        }

        /// <summary>
        /// Get the unit of work in this repository
        /// </summary>
        public IUnitOfWork UnitOfWork
        {
            get { return _unitOfWork; }
        }

        /// <summary>
        /// Get all elements
        /// </summary>
        /// <returns>List of elements</returns>
        public virtual IQueryable<TEntity> FindAll()
        {
            return _unitOfWork.Session.Query<TEntity>();
        }

        /// <summary>
        /// Get all elements (Paged)
        /// </summary>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageCount">Page count</param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> FindAll(int pageIndex, int pageCount)
        {
            if (pageIndex < 0)
                throw new ArgumentOutOfRangeException("pageIndex");

            if (pageCount < 1)
                throw new ArgumentOutOfRangeException("pageCount");

            return _unitOfWork.Session.Query<TEntity>().Skip(pageIndex * pageCount).Take(pageCount).AsQueryable();
        }

        /// <summary>
        /// Get all by query (Expression)
        /// </summary>
        /// <param name="query">Expression to evaluate</param>
        /// <returns>List of qualified entities</returns>
        public virtual IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> query)
        {
            return _unitOfWork.Session.Query<TEntity>().Where(query).AsQueryable();
        }

        /// <summary>
        /// Get all by query (Expression) (Paged)
        /// </summary>
        /// <param name="query">Expression to evaluate</param>
        /// <param name="pageIndex">Page Index</param>
        /// <param name="pageCount">Page Count</param>
        /// <returns></returns>
        public virtual IQueryable<TEntity> FindBy(Expression<Func<TEntity, bool>> query, int pageIndex, int pageCount)
        {
            if (pageIndex < 0)
                throw new ArgumentOutOfRangeException("pageIndex");

            if (pageCount < 1)
                throw new ArgumentOutOfRangeException("pageCount");

            return
                _unitOfWork.Session.Query<TEntity>().Where(query).Skip(pageIndex * pageCount).Take(pageCount).AsQueryable();
        }
    }
}